// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// go:构建aix |达尔文|蜻蜓| freebsd | | | |（js和wasm）| | linux | | netbsd | | openbsd | | solaris | | windows 

package runtime

import (
	"runtime/internal/atomic"
	"unsafe"
)

// 集成网络轮询器（独立于平台的部分）。
// 特定的实现（epoll/kqueue/port/AIX/Windows）
// 必须定义以下函数：
// 
// func netpollinit（）
// 初始化轮询器。只打过一次电话。
// 
// func netpollopen（fd uintptr，pd*pollDesc）int32 
// Arm edge为fd触发通知。pd参数用于在fd就绪时将
// 传回netpollready。返回一个errno值。
// 
// func netpollclose（fd UINTPTTR）int32 
// 禁用fd通知。返回一个errno值。
// 
// func netpoll（delta int64）gList 
// Poll网络。如果delta<0，则无限期阻塞。如果delta==0，则
// 无阻塞轮询。如果增量>0，则阻塞时间最多为增量纳秒。
// 返回通过调用netpollready生成的GoRoutine列表。
// 
// func netpollBreak（）
// 唤醒网络轮询器，假设在netpoll中被阻止。
// 
// func netpollIsPollDescriptor（fd uintptr）bool 
// 报告fd是否是轮询器使用的文件描述符。dui xiao fang

// 这些值必须与内部/poll/fd_poll_运行时中的值匹配。去
const (
	pollNoError        = 0 // 无错误
	pollErrClosing     = 1 // 描述符已关闭
	pollErrTimeout     = 2 // I/O超时
	pollErrNotPollable = 3 // 常规错误轮询描述符
)

// pollDesc包含两个二进制信号量rg和wg，分别用于驻车读写器
// goroutines。信号量可以处于以下状态：
// pdReady-io就绪通知处于挂起状态；
// goroutine通过将状态更改为nil来使用通知。
// pdWait-一辆goroutine准备停在信号灯上，但还没有停下来；
// goroutine通过将状态更改为G指针、
// 或者，并发io通知将状态更改为pdReady、
// 或者，并发超时/关闭将状态更改为nil来承诺驻车。
// G指针-goroutine在信号量上被阻塞；
// io通知或超时/关闭将状态分别更改为pdReady或nil 
// 并取消对goroutine的解析。
// nil-以上都没有。
const (
	pdReady uintptr = 1
	pdWait  uintptr = 2
)

const pollBlockSize = 4 * 1024

// 网络轮询器描述符。
// 
// 没有堆指针。
// 
// go:notinheap 
type pollDesc struct {
	link *pollDesc // 位于pollcache中，受pollcache保护。lock 
	fd   uintptr   // pollDesc使用寿命常数

	// atomicInfo保存closing、rd和wd中的位，
	// 仅在持有锁时写入，
	// 总结供netpollcheckerr使用，
	// 无法获取锁。
	// 以可能更改摘要的方式在锁定下写入这些字段后，代码必须在释放锁之前调用publishInfo 
	// 。
	// 更改字段然后调用netpollunblock的代码
	// （仍保持锁定）必须在调用netpollunblock之前调用publishInfo 
	// 因为publishInfo是
	// 阻止netpollblock重新阻塞
	// （通过更改netpollcheckerr的结果）。
	// atomicInfo还保存eventErr位，
	// 记录fd上的轮询事件是否出错；
	// 原子信息是这一点的唯一真相来源。
	atomicInfo atomic.Uint32 // atomic pollInfo 

	// rg，wg以原子方式访问，并保留g指针。
	// （此处使用atomic.uintpttr与其他地方使用guintpttr类似。）
	rg atomic.Uintptr // pdReady，pdWait，G等待读取或无
	wg atomic.Uintptr // pdReady，pdWait，G等待写入或为零

	lock    mutex // 保护以下字段
	closing bool
	user    uint32    // 用户可设置cookie 
	rseq    uintptr   // 保护不受过时的读取计时器
	rt      timer     // 读取截止时间计时器（如果rt.f！=nil设置）
	rd      int64     // 读取截止时间（未来的一个纳米时间，过期时为-1）
	wt      timer     // 写入截止时间计时器
	wd      int64     // 写入截止时间（将来是一个纳米时间，过期时为-1）
	self    *pollDesc // 间接接口存储。见（*pollDesc）。makeArg。
}

// pollInfo是netpollcheckerr所需的位，以原子方式存储，
// 主要是复制状态，在pollDesc的锁定下操作。
// 唯一的例外是pollEventErr位，它仅在pollInfo中维护
// 。
type pollInfo uint32

const (
	pollClosing = 1 << iota
	pollEventErr
	pollExpiredReadDeadline
	pollExpiredWriteDeadline
)

func (i pollInfo) closing() bool              { return i&pollClosing != 0 }
func (i pollInfo) eventErr() bool             { return i&pollEventErr != 0 }
func (i pollInfo) expiredReadDeadline() bool  { return i&pollExpiredReadDeadline != 0 }
func (i pollInfo) expiredWriteDeadline() bool { return i&pollExpiredWriteDeadline != 0 }

// info返回pd对应的pollInfo。
func (pd *pollDesc) info() pollInfo {
	return pollInfo(pd.atomicInfo.Load())
}

// publishInfo更新pd。atomicInfo（由pd.info返回）
// 使用pd中的其他值。
// 必须在持有pd时调用它。lock，
// 并且必须在更改任何可能影响信息位的
// 后调用它。
// 在实践中，这意味着在更改关闭
// 或将rd或wd从<0更改为>=0后。
func (pd *pollDesc) publishInfo() {
	var info uint32
	if pd.closing {
		info |= pollClosing
	}
	if pd.rd < 0 {
		info |= pollExpiredReadDeadline
	}
	if pd.wd < 0 {
		info |= pollExpiredWriteDeadline
	}

	// 设置除pollEventErr位之外的所有x。
	x := pd.atomicInfo.Load()
	for !pd.atomicInfo.CompareAndSwap(x, (x&pollEventErr)|info) {
		x = pd.atomicInfo.Load()
	}
}

// setEventErr设置pd的结果。信息（）。eventErr（）到b.
func (pd *pollDesc) setEventErr(b bool) {
	x := pd.atomicInfo.Load()
	for (x&pollEventErr != 0) != b && !pd.atomicInfo.CompareAndSwap(x, x^pollEventErr) {
		x = pd.atomicInfo.Load()
	}
}

type pollCache struct {
	lock  mutex
	first *pollDesc
	// PollDesc对象必须是类型稳定的，
	// 因为在关闭/重用描述符后，我们可以从epoll/kqueue 
	// 获得就绪通知。
	// 使用seq变量检测过期通知，
	// 当截止日期更改或重新使用描述符时，seq将递增。
}

var (
	netpollInitLock mutex
	netpollInited   uint32

	pollcache      pollCache
	netpollWaiters uint32
)

// go:linkname poll_runtime_pollServerInit internal/poll。运行时\u pollServerInit 
func poll_runtime_pollServerInit() {
	netpollGenericInit()
}

func netpollGenericInit() {
	if atomic.Load(&netpollInited) == 0 {
		lockInit(&netpollInitLock, lockRankNetpollInit)
		lock(&netpollInitLock)
		if netpollInited == 0 {
			netpollinit()
			atomic.Store(&netpollInited, 1)
		}
		unlock(&netpollInitLock)
	}
}

func netpollinited() bool {
	return atomic.Load(&netpollInited) != 0
}

// go:linkname poll\u运行时\u isPollServerDescriptor internal/poll。运行时\u isPollServerDescriptor 

// 轮询\u运行时\u isPollServerDescriptor报告fd是否是netpoll正在使用的
// 描述符。
func poll_runtime_isPollServerDescriptor(fd uintptr) bool {
	return netpollIsPollDescriptor(fd)
}

// go:linkname poll\u runtime\u pollOpen internal/poll。运行时\u pollOpen 
func poll_runtime_pollOpen(fd uintptr) (*pollDesc, int) {
	pd := pollcache.alloc()
	lock(&pd.lock)
	wg := pd.wg.Load()
	if wg != 0 && wg != pdReady {
		throw("runtime: blocked write on free polldesc")
	}
	rg := pd.rg.Load()
	if rg != 0 && rg != pdReady {
		throw("runtime: blocked read on free polldesc")
	}
	pd.fd = fd
	pd.closing = false
	pd.setEventErr(false)
	pd.rseq++
	pd.rg.Store(0)
	pd.rd = 0
	pd.wseq++
	pd.wg.Store(0)
	pd.wd = 0
	pd.self = pd
	pd.publishInfo()
	unlock(&pd.lock)

	errno := netpollopen(fd, pd)
	if errno != 0 {
		pollcache.free(pd)
		return nil, int(errno)
	}
	return pd, 0
}

// go:linkname poll\u运行时\u pollClose internal/poll。运行时轮询关闭
func poll_runtime_pollClose(pd *pollDesc) {
	if !pd.closing {
		throw("runtime: close polldesc w/o unblock")
	}
	wg := pd.wg.Load()
	if wg != 0 && wg != pdReady {
		throw("runtime: blocked write on closing polldesc")
	}
	rg := pd.rg.Load()
	if rg != 0 && rg != pdReady {
		throw("runtime: blocked read on closing polldesc")
	}
	netpollclose(pd.fd)
	pollcache.free(pd)
}

func (c *pollCache) free(pd *pollDesc) {
	lock(&c.lock)
	pd.link = c.first
	c.first = pd
	unlock(&c.lock)
}

// 轮询运行时轮询重置，这是内部/轮询。runtime_pollReset，
// 为模式中的轮询准备描述符，即“r”或“w”。
// 返回错误代码；上述代码已定义。
// go:linkname poll\u runtime\u pollReset internal/poll。运行时轮询重置
func poll_runtime_pollReset(pd *pollDesc, mode int) int {
	errcode := netpollcheckerr(pd, int32(mode))
	if errcode != pollNoError {
		return errcode
	}
	if mode == 'r' {
		pd.rg.Store(0)
	} else if mode == 'w' {
		pd.wg.Store(0)
	}
	return pollNoError
}

// poll\u运行时轮询等待，这是内部/轮询。runtime_pollWait，
// 等待描述符准备好读取或写入，
// 根据模式，即“r”或“w”。
// 返回错误代码；上述代码已定义。
// go:linkname poll\u runtime\u pollWait internal/poll。runtime_pollWait 
func poll_runtime_pollWait(pd *pollDesc, mode int) int {
	errcode := netpollcheckerr(pd, int32(mode))
	if errcode != pollNoError {
		return errcode
	}
	// 目前只有Solaris、illumos和AIX使用级别触发IO。
	if GOOS == "solaris" || GOOS == "illumos" || GOOS == "aix" {
		netpollarm(pd, mode)
	}
	for !netpollblock(pd, int32(mode), false) {
		errcode = netpollcheckerr(pd, int32(mode))
		if errcode != pollNoError {
			return errcode
		}
		// 如果超时触发并解除了我们的阻止，可能会发生，
		// 但在我们有机会运行之前，超时已重置。
		// 假装没有发生，然后重试。
	}
	return pollNoError
}

// go:linkname poll_runtime_pollwait取消内部/轮询。runtime_PollWaitCancelled 
func poll_runtime_pollWaitCanceled(pd *pollDesc, mode int) {
	// 此函数仅在尝试取消
	// 挂起的异步IO操作失败后在windows上使用。等待I已完成，忽略关闭或超时。
	for !netpollblock(pd, int32(mode), true) {
	}
}

// go:linkname poll_runtime_pollsetDaildate internal/poll。runtime_pollSetDeadline 
func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) {
	lock(&pd.lock)
	if pd.closing {
		unlock(&pd.lock)
		return
	}
	rd0, wd0 := pd.rd, pd.wd
	combo0 := rd0 > 0 && rd0 == wd0
	if d > 0 {
		d += nanotime()
		if d <= 0 {
			// 如果用户将来有截止日期，但延迟计算
			// 溢出，则将截止日期设置为最大可能值。
			d = 1<<63 - 1
		}
	}
	if mode == 'r' || mode == 'r'+'w' {
		pd.rd = d
	}
	if mode == 'w' || mode == 'r'+'w' {
		pd.wd = d
	}
	pd.publishInfo()
	combo := pd.rd > 0 && pd.rd == pd.wd
	rtf := netpollReadDeadline
	if combo {
		rtf = netpollDeadline
	}
	if pd.rt.f == nil {
		if pd.rd > 0 {
			pd.rt.f = rtf
			// 将当前序列复制到计时器参数中。
			// Timer func将根据当前描述符seq检查seq，
			// 如果它们不同，则重新使用描述符或重置计时器。
			pd.rt.arg = pd.makeArg()
			pd.rt.seq = pd.rseq
			resettimer(&pd.rt, pd.rd)
		}
	} else if pd.rd != rd0 || combo != combo0 {
		pd.rseq++ // 使当前计时器无效
		if pd.rd > 0 {
			modtimer(&pd.rt, pd.rd, 0, rtf, pd.makeArg(), pd.rseq)
		} else {
			deltimer(&pd.rt)
			pd.rt.f = nil
		}
	}
	if pd.wt.f == nil {
		if pd.wd > 0 && !combo {
			pd.wt.f = netpollWriteDeadline
			pd.wt.arg = pd.makeArg()
			pd.wt.seq = pd.wseq
			resettimer(&pd.wt, pd.wd)
		}
	} else if pd.wd != wd0 || combo != combo0 {
		pd.wseq++ // 使当前计时器无效
		if pd.wd > 0 && !combo {
			modtimer(&pd.wt, pd.wd, 0, netpollWriteDeadline, pd.makeArg(), pd.wseq)
		} else {
			deltimer(&pd.wt)
			pd.wt.f = nil
		}
	}
	// 如果我们在过去设置了新的截止日期，请取消阻止当前挂起的IO（如果有）。
	// 注意pd。上面已经在修改rd和wd后立即调用了publishInfo。
	var rg, wg *g
	if pd.rd < 0 {
		rg = netpollunblock(pd, 'r', false)
	}
	if pd.wd < 0 {
		wg = netpollunblock(pd, 'w', false)
	}
	unlock(&pd.lock)
	if rg != nil {
		netpollgoready(rg, 3)
	}
	if wg != nil {
		netpollgoready(wg, 3)
	}
}

// go:linkname poll\u runtime\u pollunk internal/poll。运行时_pollunblockabcdefg
func poll_runtime_pollUnblock(pd *pollDesc) {
	lock(&pd.lock)
	if pd.closing {
		throw("runtime: unblock on closing polldesc")
	}
	pd.closing = true
	pd.rseq++
	pd.wseq++
	var rg, wg *g
	pd.publishInfo()
	rg = netpollunblock(pd, 'r', false)
	wg = netpollunblock(pd, 'w', false)
	if pd.rt.f != nil {
		deltimer(&pd.rt)
		pd.rt.f = nil
	}
	if pd.wt.f != nil {
		deltimer(&pd.wt)
		pd.wt.f = nil
	}
	unlock(&pd.lock)
	if rg != nil {
		netpollgoready(rg, 3)
	}
	if wg != nil {
		netpollgoready(wg, 3)
	}
}

// 它声明与pd关联的fd已准备好进行I/O。
// toRun参数用于构建Goroutine列表，以从netpoll返回
// 。mode参数为'r'、'w'或'r'+'w'表示
// fd是否准备好读取或写入，或两者兼而有之。
// 
// 这可能会在世界停止时运行，因此不允许写入障碍。
// go:nowritebarrier 
func netpollready(toRun *gList, pd *pollDesc, mode int32) {
	var rg, wg *g
	if mode == 'r' || mode == 'r'+'w' {
		rg = netpollunblock(pd, 'r', true)
	}
	if mode == 'w' || mode == 'r'+'w' {
		wg = netpollunblock(pd, 'w', true)
	}
	if rg != nil {
		toRun.push(rg)
	}
	if wg != nil {
		toRun.push(wg)
	}
}

func netpollcheckerr(pd *pollDesc, mode int32) int {
	info := pd.info()
	if info.closing() {
		return pollErrClosing
	}
	if (mode == 'r' && info.expiredReadDeadline()) || (mode == 'w' && info.expiredWriteDeadline()) {
		return pollErrTimeout
	}
	// 仅在读取事件时报告事件扫描错误。
	// 写入事件的错误将在后续的
	// 写入调用中捕获，该调用能够报告更具体的错误。
	if mode == 'r' && info.eventErr() {
		return pollErrNotPollable
	}
	return pollNoError
}

func netpollblockcommit(gp *g, gpp unsafe.Pointer) bool {
	r := atomic.Casuintptr((*uintptr)(gpp), pdWait, uintptr(unsafe.Pointer(gp)))
	if r {
		// 。
		// 调度程序使用此命令来决定是否阻止
		// 如果没有其他事情要做，则等待轮询器。
		atomic.Xadd(&netpollWaiters, 1)
	}
	return r
}

func netpollgoready(gp *g, traceskip int) {
	atomic.Xadd(&netpollWaiters, -1)
	goready(gp, traceskip+1)
}

// 如果IO已准备就绪，则返回true；如果timedout或closed，则返回false 
// waitio-仅等待完成的IO，忽略错误
// 禁止在相同模式下同时调用netpollblock，因为对于每个模式，pollDesc 
// 只能保存一个等待的goroutine。
func netpollblock(pd *pollDesc, mode int32, waitio bool) bool {
	gpp := &pd.rg
	if mode == 'w' {
		gpp = &pd.wg
	}

	// 将gpp信号量设置为pdWait 
	for {
		// 如果已经准备好，则使用通知。
		if gpp.CompareAndSwap(pdReady, 0) {
			return true
		}
		if gpp.CompareAndSwap(0, pdWait) {
			break
		}

		// 再次检查此文件是否已损坏；否则我们将永远循环
		// 。
		if v := gpp.Load(); v != pdReady && v != 0 {
			throw("runtime: double wait")
		}
	}

	// 将gpp设置为pdWait后需要重新检查错误状态
	// 这是必要的，因为运行时\u pollUnblock/runtime\u pollsetDailate/deadlineimpl 
	// 执行相反的操作：存储到closing/rd/wd，publishInfo，加载rg/wg 
	if waitio || netpollcheckerr(pd, mode) == pollNoError {
		gopark(netpollblockcommit, unsafe.Pointer(gpp), waitReasonIOWait, traceEvGoBlockNet, 5)
	}
	// 小心不要丢失并发pdReady通知
	old := gpp.Swap(0)
	if old > pdWait {
		throw("runtime: corrupted polldesc")
	}
	return old == pdReady
}

func netpollunblock(pd *pollDesc, mode int32, ioready bool) *g {
	gpp := &pd.rg
	if mode == 'w' {
		gpp = &pd.wg
	}

	for {
		old := gpp.Load()
		if old == pdReady {
			return nil
		}
		if old == 0 && !ioready {
			// 仅将pdReady设置为ioready。运行时_pollwaitabcdefg
			return nil
		}
		var new uintptr
		if ioready {
			new = pdReady
		}
		if gpp.CompareAndSwap(old, new) {
			if old == pdWait {
				old = 0
			}
			return (*g)(unsafe.Pointer(old))
		}
	}
}

func netpolldeadlineimpl(pd *pollDesc, seq uintptr, read, write bool) {
	lock(&pd.lock)
	// 设置计时器时，Seq arg为Seq。
	// 如果它过时了，忽略计时器事件。
	currentSeq := pd.rseq
	if !read {
		currentSeq = pd.wseq
	}
	if seq != currentSeq {
		// 重新使用了描述符或重置了计时器。
		unlock(&pd.lock)
		return
	}
	var rg *g
	if read {
		if pd.rd <= 0 || pd.rt.f == nil {
			throw("runtime: inconsistent read deadline")
		}
		pd.rd = -1
		pd.publishInfo()
		rg = netpollunblock(pd, 'r', false)
	}
	var wg *g
	if write {
		if pd.wd <= 0 || pd.wt.f == nil && !read {
			throw("runtime: inconsistent write deadline")
		}
		pd.wd = -1
		pd.publishInfo()
		wg = netpollunblock(pd, 'w', false)
	}
	unlock(&pd.lock)
	if rg != nil {
		netpollgoready(rg, 0)
	}
	if wg != nil {
		netpollgoready(wg, 0)
	}
}

func netpollDeadline(arg any, seq uintptr) {
	netpolldeadlineimpl(arg.(*pollDesc), seq, true, true)
}

func netpollReadDeadline(arg any, seq uintptr) {
	netpolldeadlineimpl(arg.(*pollDesc), seq, true, false)
}

func netpollWriteDeadline(arg any, seq uintptr) {
	netpolldeadlineimpl(arg.(*pollDesc), seq, false, true)
}

func (c *pollCache) alloc() *pollDesc {
	lock(&c.lock)
	if c.first == nil {
		const pdSize = unsafe.Sizeof(pollDesc{})
		n := pollBlockSize / pdSize
		if n == 0 {
			n = 1
		}
		// 必须在非GC内存中，因为只能从epoll/kqueue内部引用
		// 。
		mem := persistentalloc(n*pdSize, 0, &memstats.other_sys)
		for i := uintptr(0); i < n; i++ {
			pd := (*pollDesc)(add(mem, i*pdSize))
			pd.link = c.first
			c.first = pd
		}
	}
	pd := c.first
	c.first = pd.link
	lockInit(&pd.lock, lockRankPollDesc)
	unlock(&c.lock)
	return pd
}

// makeArg将pd转换为接口{}。
// makeArg不进行任何分配。通常，这种转换需要分配，因为指向
// go:notinheap类型（pollDesc是）的指针必须间接存储在接口中。见第42076期。
func (pd *pollDesc) makeArg() (i any) {
	x := (*eface)(unsafe.Pointer(&i))
	x._type = pdType
	x.data = unsafe.Pointer(&pd.self)
	return
}

var (
	pdEface any    = (*pollDesc)(nil)
	pdType  *_type = efaceOf(&pdEface)._type
)
